package edu.vt.ece.rt.benchmark.list;

import java.util.ArrayList;

import java.util.HashMap;
import java.util.Map;

import org.jgroups.annotations.LocalAddress;

import edu.vt.ece.rt.dstm.Context;
import edu.vt.ece.rt.dstm.ContextDelegator;
import edu.vt.ece.rt.dstm.QObject;
import edu.vt.ece.rt.dstm.TMProxy;
import edu.vt.ece.rt.quorum.ClustersManager;
import edu.vt.ece.rt.quorum.Resource;
import edu.vt.ece.rt.util.Logger.Logger;
import edu.vt.ece.rt.benchmark.BenchmarkThread;
import edu.vt.ece.rt.benchmark.bank.BankAccount;


public class List extends QObject {

	int val;
	String id;
	String nextId;
	
	public List() {
		
	}
	
	public List(String Id ,int v, String next) {
		id = Id;
		val = v;
		nextId = next;
		TMProxy.getTMProxy().publishObject(this);
		Resource.putQObject(getId(), this);
	}
	
	void setNext(String Id) {
		nextId = Id;
	}
	
	String getNext() {
		return nextId;
	}
	
	int getVal() {
		return val;
	}

	void setVal(int v) {
		val = v;
	}
	
	@Override
	public QObject clone() {
		List l = new List();
		l.setId(getId());
		l.setVersion(getVersion());
		l.setNext(getNext());
		l.setVal(getVal());
		return l;
	}

	@Override
	public String getId() {
		return id;
	}
	
	public void setId(String id) {
		this.id = id;
	}
	

	public static Boolean find(int val, Context parentContext) {

		Boolean found = null;
		
		for (int i = 0; i < 0x7fffffff; i++) {
			if (i > 0) {
				((BenchmarkThread)Thread.currentThread()).incrementAborts();
			}
			
			if (i > 100) {
				System.out.println("I am stuck ["+ClustersManager.baseClusterId+"]"+ " for "+i);
			}
			
			try {

				Logger.debug("Sleeping for "+i*(ClustersManager.baseClusterId+1)*1000L);
				Thread.sleep(i*(ClustersManager.baseClusterId+1)*100L);
				Logger.debug("Waking up from sleep ["+ClustersManager.baseClusterId+"]");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("find element in list");
			context.sanityNested();
			
			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				
				ArrayList<List> pair = getNode(val, context);
				List curr = pair.get(1);				
				
				found = curr != null && curr.getVal() == val;
//				if (found) {
//					System.out.println("Found val="+val);
//				} else {
//					System.out.println("Not found val="+val);
//				}
				
				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return found;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		Logger.fetal("Unable to complete transaction in given number of tries");
		
		return found;
	}
	
	public static Boolean add(int val, Context parentContext) {
		
		for (int i = 0; i < 0x7fffffff; i++) {
			if (i > 0) {
				((BenchmarkThread)Thread.currentThread()).incrementAborts();
			}

			if (i > 100) {
				System.out.println("I am stuck ["+ClustersManager.baseClusterId+"]"+ " for "+i);
			}
			
			try {

				Logger.debug("Sleeping for "+i*(ClustersManager.baseClusterId+1)*1000L);
				Thread.sleep(i*(ClustersManager.baseClusterId+1)*100L);
				Logger.debug("Waking up from sleep ["+ClustersManager.baseClusterId+"]");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("add element "+val+" in list");
			context.sanityNested();
			
			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				
				ArrayList<List> pair = getNode(val, context);
				List curr = pair.get(1);
				List prev = pair.get(0);
				
				/* skip case where first element for simplicity*/
				if (prev == null || curr == null) {
					/* fall through */
				} else {
					/* get object for write */
					prev = (List) TMProxy.getTMProxy().write(prev.getId(), context.getContextId());

					/* increment the found value by 1 */
					String id = QObject.getRandomId();
					List newList = new List();
					newList.setVal(val);
					newList.setNext(curr.getId());
					newList.setId(id);
					context.addToCreateSet(newList);

					prev.setNext(newList.getId());
				}
				
				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return true;
				}
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		Logger.fetal("Unable to complete transaction in given number of tries");
		
		return false;
	}

	public static Boolean delete(int val, Context parentContext) {

		
		for (int i = 0; i < 0x7fffffff; i++) {
			if (i > 0) {
				((BenchmarkThread)Thread.currentThread()).incrementAborts();
			}
			
			if (i > 100) {
				System.out.println("I am stuck ["+ClustersManager.baseClusterId+"]"+ " for "+i);
			}
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("delete element "+val +" in list");
			context.sanityNested();
			
			try {

				Logger.debug("Sleeping for "+i*(ClustersManager.baseClusterId+1)*1000L);
				Thread.sleep(i*(ClustersManager.baseClusterId+1)*100L);
				Logger.debug("Waking up from sleep ["+ClustersManager.baseClusterId+"]");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			
			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				
				ArrayList<List> pair = getNode(val, context);
				List curr = pair.get(1);				
				List prev = pair.get(0);
				String newNext = null;

				/* skip case where first element for simplicity*/
				if (prev == null || curr == null) {
					/* fall through */
				} else if (curr.getVal() == val) {
					/* get object for write */
					newNext = curr.getNext();
					prev = (List) TMProxy.getTMProxy().write(prev.getId(), context.getContextId());
					prev.setNext(newNext);
				} else {
					/* fall through */
				}
				
				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return true;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		Logger.fetal("Unable to complete transaction in given number of tries");
		
		return false;
	}

	
	static ArrayList<List> getNode(int val, Context context) {
		String headId = "0";
		String currId = headId;
		List prev = null;
		List curr = null;
		try {
			do {
				prev = curr;
				curr = (List) TMProxy.getTMProxy().read(currId, context.getContextId());
//				System.out.println("Found in getNode() val="+curr.getVal());
			} while(curr.getVal() < val && (currId = curr.getNext()) != null);

			curr = (curr.getVal() >= val)? curr : null;
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		ArrayList<List> arr =new ArrayList<List>();
		arr.add(0, prev);
		arr.add(1, curr);
//		if (curr != null)
////			System.out.println("Returning curr node as "+curr.getVal());
//		else 
//			System.out.println("Node not found");
		return arr;
	}
	
	static int sum(Context parentContext) {

		int sum = 0;
		
		for (int i = 0; i < 0x7fffffff; i++) {
			if (i > 0) {
				((BenchmarkThread)Thread.currentThread()).incrementAborts();
			}
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("delete element in list");
			context.sanityNested();

			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);


			String headId = "0";
			String currId = headId;
			List curr = null;
			List.find(Integer.MAX_VALUE,null);
			try {
				do {
					curr = (List)Resource.getQObject(currId);
					sum= sum + curr.getVal();
				} while((currId = curr.getNext()) != null);

			} catch (Exception e) {
				e.printStackTrace();
			}
			
		
		
		try {
			if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
				context.unsetParent(parentContext);
				return sum;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		}	
		return sum;
}
	


}